/*
 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 1996,1999 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *	  src/backend/utils/adt/inet_net_pton.c
 */

#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "Id: inet_net_pton.c,v 1.4.2.3 2004/03/17 00:40:11 marka Exp $";
#endif

#include "postgres.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>

#include "utils/builtins.h" /* pgrminclude 忽略 */	/* 在某些平台上是必需的 */
#include "utils/inet.h"


static int	fc_inet_net_pton_ipv4(const char *fc_src, u_char *fc_dst);
static int	fc_inet_cidr_pton_ipv4(const char *fc_src, u_char *fc_dst, size_t fc_size);
static int	fc_inet_net_pton_ipv6(const char *fc_src, u_char *fc_dst);
static int	fc_inet_cidr_pton_ipv6(const char *fc_src, u_char *fc_dst, size_t fc_size);


/*
 * int
 * pg_inet_net_pton(af, src, dst, size)
 *	将网络号从表示格式转换为网络格式。
 *	接受十六进制八位字节、十六进制字符串、十进制八位字节和 /CIDR。
 *	"size" 以字节为单位，描述 "dst"。
 * 返回：
 *	位数，要么是隐式分类，要么通过 /CIDR 指定，
 *	如果发生某些故障则为 -1（检查 errno）。ENOENT 表示它不是
 *	有效的网络规范。
 * 作者：
 *	Paul Vixie (ISC)，1996年6月
 *
 * 更改：
 *	我添加了 inet_cidr_pton 函数（同样来自 Paul）并更改
 *	名称以反映它们的当前使用。
 *
 */
int pg_inet_net_pton(int fc_af, const char *fc_src, void *fc_dst, size_t fc_size)
{
	switch (fc_af)
	{
		case PGSQL_AF_INET:
			return fc_size == -1 ?
				fc_inet_net_pton_ipv4(fc_src, fc_dst) :
				fc_inet_cidr_pton_ipv4(fc_src, fc_dst, fc_size);
		case PGSQL_AF_INET6:
			return fc_size == -1 ?
				fc_inet_net_pton_ipv6(fc_src, fc_dst) :
				fc_inet_cidr_pton_ipv6(fc_src, fc_dst, fc_size);
		default:
			errno = EAFNOSUPPORT;
			return -1;
	}
}

/*
 * static int
 * inet_cidr_pton_ipv4(src, dst, size)
 *	将 IPv4 网络号从表示格式转换为网络格式。
 *	接受十六进制八位字节、十六进制字符串、十进制八位字节和 /CIDR。
 *	"size" 以字节为单位，描述 "dst"。
 * 返回：
 *	位数，要么是隐式分类，要么通过 /CIDR 指定，
 *	如果发生某些故障则为 -1（检查 errno）。ENOENT 表示它不是
 *	有效的 IPv4 网络规范。
 * 注意：
 *	假定网络字节顺序。这意味着 192.5.5.240/28 在其第四个八位字节中有
 *	0b11110000。
 * 作者：
 *	Paul Vixie (ISC)，1996年6月
 */
static int fc_inet_cidr_pton_ipv4(const char *fc_src, u_char *fc_dst, size_t fc_size)
{
	static const char fc_xdigits[] = "0123456789abcdef";
	static const char fc_digits[] = "0123456789";
	int			fc_n,
				fc_ch,
				fc_tmp = 0,
				fc_dirty,
				fc_bits;
	const u_char *fc_odst = fc_dst;

	fc_ch = *fc_src++;
	if (fc_ch == '0' && (fc_src[0] == 'x' || fc_src[0] == 'X')
		&& isxdigit((unsigned char) fc_src[1]))
	{
		/* 以十六进制表示：获取 nybble 字符串。 */
		if (fc_size <= 0U)
			goto emsgsize;
		fc_dirty = 0;
		fc_src++;					/* 跳过 x 或 X。 */
		while ((fc_ch = *fc_src++) != '\0' && isxdigit((unsigned char) fc_ch))
		{
			if (isupper((unsigned char) fc_ch))
				fc_ch = tolower((unsigned char) fc_ch);
			fc_n = strchr(fc_xdigits, fc_ch) - fc_xdigits;
			assert(fc_n >= 0 && fc_n <= 15);
			if (fc_dirty == 0)
				fc_tmp = fc_n;
			else
				fc_tmp = (fc_tmp << 4) | fc_n;
			if (++fc_dirty == 2)
			{
				if (fc_size-- <= 0U)
					goto emsgsize;
				*fc_dst++ = (u_char) fc_tmp;
				fc_dirty = 0;
			}
		}
		if (fc_dirty)
		{						/* 奇数的尾随 nybble？ */
			if (fc_size-- <= 0U)
				goto emsgsize;
			*fc_dst++ = (u_char) (fc_tmp << 4);
		}
	}
	else if (isdigit((unsigned char) fc_ch))
	{
		/* 十进制：吃点缀的数字字符串。*/
		for (;;)
		{
			fc_tmp = 0;
			do
			{
				fc_n = strchr(fc_digits, fc_ch) - fc_digits;
				assert(fc_n >= 0 && fc_n <= 9);
				fc_tmp *= 10;
				fc_tmp += fc_n;
				if (fc_tmp > 255)
					goto enoent;
			} while ((fc_ch = *fc_src++) != '\0' &&
					 isdigit((unsigned char) fc_ch));
			if (fc_size-- <= 0U)
				goto emsgsize;
			*fc_dst++ = (u_char) fc_tmp;
			if (fc_ch == '\0' || fc_ch == '/')
				break;
			if (fc_ch != '.')
				goto enoent;
			fc_ch = *fc_src++;
			if (!isdigit((unsigned char) fc_ch))
				goto enoent;
		}
	}
	else
		goto enoent;

	fc_bits = -1;
	if (fc_ch == '/' && isdigit((unsigned char) fc_src[0]) && fc_dst > fc_odst)
	{
		/* CIDR宽度说明符。 之后不能跟随任何内容。*/
		fc_ch = *fc_src++;			/* 跳过/。*/
		fc_bits = 0;
		do
		{
			fc_n = strchr(fc_digits, fc_ch) - fc_digits;
			assert(fc_n >= 0 && fc_n <= 9);
			fc_bits *= 10;
			fc_bits += fc_n;
		} while ((fc_ch = *fc_src++) != '\0' && isdigit((unsigned char) fc_ch));
		if (fc_ch != '\0')
			goto enoent;
		if (fc_bits > 32)
			goto emsgsize;
	}

	/* 除非我们预取了EOS，否则会有火焰般的死亡和毁灭。*/
	if (fc_ch != '\0')
		goto enoent;

	/* 如果没有写入目的地，则我们没有找到地址。*/
	if (fc_dst == fc_odst)
		goto enoent;
	/* 如果未给出CIDR说明符，则根据网络类别推断宽度。*/
	if (fc_bits == -1)
	{
		if (*fc_odst >= 240)		/* Class E */
			fc_bits = 32;
		else if (*fc_odst >= 224)	/* Class D */
			fc_bits = 8;
		else if (*fc_odst >= 192)	/* Class C */
			fc_bits = 24;
		else if (*fc_odst >= 128)	/* Class B */
			fc_bits = 16;
		else
			/* Class A */
			fc_bits = 8;
		/* 如果输入的掩码比指定的八位字节窄，则扩大。*/
		if (fc_bits < ((fc_dst - fc_odst) * 8))
			fc_bits = (fc_dst - fc_odst) * 8;

		/* 
		 * 如果没有为类D地址指定额外的位
		 * 则将位数调整为4。
		 */
		if (fc_bits == 8 && *fc_odst == 224)
			fc_bits = 4;
	}
	/* 扩展网络以覆盖实际掩码。*/
	while (fc_bits > ((fc_dst - fc_odst) * 8))
	{
		if (fc_size-- <= 0U)
			goto emsgsize;
		*fc_dst++ = '\0';
	}
	return fc_bits;

enoent:
	errno = ENOENT;
	return -1;

emsgsize:
	errno = EMSGSIZE;
	return -1;
}

/* 
 * int
 * inet_net_pton_ipv4(af, src, dst, *bits)
 *	将网络地址从表示格式转换为网络格式。
 *	接受inet_pton()的输入以用于此“af”以及后面的“/CIDR”。
 *	“dst”假定大到足以适应其“af”。  “bits”被设置为
 *	/CIDR前缀长度，可以有默认值（例如，IPv4的/32）。
 * 返回：
 *	-1如果发生错误（检查errno；ENOENT表示格式错误）。
 *	0如果成功转换完成。
 * 注意：
 *	192.5.5.1/28具有非零主机部分，意味着它不是一个网络
 *	正如inet_cidr_pton()所要求的，但它可以是带有
 *	包含的掩码的主机地址。
 * 作者：
 *	Paul Vixie（ISC），1998年10月
 */
static int fc_inet_net_pton_ipv4(const char *fc_src, u_char *fc_dst)
{
	static const char fc_digits[] = "0123456789";
	const u_char *fc_odst = fc_dst;
	int			fc_n,
				fc_ch,
				fc_tmp,
				fc_bits;
	size_t		fc_size = 4;

	/* 获取尾数。*/
	while (fc_ch = *fc_src++, isdigit((unsigned char) fc_ch))
	{
		fc_tmp = 0;
		do
		{
			fc_n = strchr(fc_digits, fc_ch) - fc_digits;
			assert(fc_n >= 0 && fc_n <= 9);
			fc_tmp *= 10;
			fc_tmp += fc_n;
			if (fc_tmp > 255)
				goto enoent;
		} while ((fc_ch = *fc_src++) != '\0' && isdigit((unsigned char) fc_ch));
		if (fc_size-- == 0)
			goto emsgsize;
		*fc_dst++ = (u_char) fc_tmp;
		if (fc_ch == '\0' || fc_ch == '/')
			break;
		if (fc_ch != '.')
			goto enoent;
	}

	/* 如果有的话，获取前缀长度。*/
	fc_bits = -1;
	if (fc_ch == '/' && isdigit((unsigned char) fc_src[0]) && fc_dst > fc_odst)
	{
		/* CIDR宽度说明符。 之后不能跟随任何内容。*/
		fc_ch = *fc_src++;			/* 跳过/。*/
		fc_bits = 0;
		do
		{
			fc_n = strchr(fc_digits, fc_ch) - fc_digits;
			assert(fc_n >= 0 && fc_n <= 9);
			fc_bits *= 10;
			fc_bits += fc_n;
		} while ((fc_ch = *fc_src++) != '\0' && isdigit((unsigned char) fc_ch));
		if (fc_ch != '\0')
			goto enoent;
		if (fc_bits > 32)
			goto emsgsize;
	}

	/* 除非我们预取了EOS，否则会有火焰般的死亡和毁灭。*/
	if (fc_ch != '\0')
		goto enoent;

	/* 前缀长度只能在指定四个八位字节的情况下默认为/32。*/
	if (fc_bits == -1)
	{
		if (fc_dst - fc_odst == 4)
			fc_bits = 32;
		else
			goto enoent;
	}

	/* 如果没有写入目的地，则我们没有找到地址。*/
	if (fc_dst == fc_odst)
		goto enoent;

	/* 如果前缀长度过度指定了尾数，生活就很糟糕。*/
	if ((fc_bits / 8) > (fc_dst - fc_odst))
		goto enoent;

	/* 扩展地址到四个八位字节。*/
	while (fc_size-- > 0)
		*fc_dst++ = 0;

	return fc_bits;

enoent:
	errno = ENOENT;
	return -1;

emsgsize:
	errno = EMSGSIZE;
	return -1;
}

static int fc_getbits(const char *fc_src, int *fc_bitsp)
{
	static const char fc_digits[] = "0123456789";
	int			fc_n;
	int			fc_val;
	char		fc_ch;

	fc_val = 0;
	fc_n = 0;
	while ((fc_ch = *fc_src++) != '\0')
	{
		const char *fc_pch;

		fc_pch = strchr(fc_digits, fc_ch);
		if (fc_pch != NULL)
		{
			if (fc_n++ != 0 && fc_val == 0)	/* 无前导零*/
				return 0;
			fc_val *= 10;
			fc_val += (fc_pch - fc_digits);
			if (fc_val > 128)		/* range */
				return 0;
			continue;
		}
		return 0;
	}
	if (fc_n == 0)
		return 0;
	*fc_bitsp = fc_val;
	return 1;
}

static int fc_getv4(const char *fc_src, u_char *fc_dst, int *fc_bitsp)
{
	static const char fc_digits[] = "0123456789";
	u_char	   *fc_odst = fc_dst;
	int			fc_n;
	u_int		fc_val;
	char		fc_ch;

	fc_val = 0;
	fc_n = 0;
	while ((fc_ch = *fc_src++) != '\0')
	{
		const char *fc_pch;

		fc_pch = strchr(fc_digits, fc_ch);
		if (fc_pch != NULL)
		{
			if (fc_n++ != 0 && fc_val == 0)	/* 无前导零*/
				return 0;
			fc_val *= 10;
			fc_val += (fc_pch - fc_digits);
			if (fc_val > 255)		/* range */
				return 0;
			continue;
		}
		if (fc_ch == '.' || fc_ch == '/')
		{
			if (fc_dst - fc_odst > 3) /* 八位字节过多？*/
				return 0;
			*fc_dst++ = fc_val;
			if (fc_ch == '/')
				return fc_getbits(fc_src, fc_bitsp);
			fc_val = 0;
			fc_n = 0;
			continue;
		}
		return 0;
	}
	if (fc_n == 0)
		return 0;
	if (fc_dst - fc_odst > 3)			/* 八位字节过多？*/
		return 0;
	*fc_dst++ = fc_val;
	return 1;
}

static int fc_inet_net_pton_ipv6(const char *fc_src, u_char *fc_dst)
{
	return fc_inet_cidr_pton_ipv6(fc_src, fc_dst, 16);
}

#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#define NS_INADDRSZ 4

static int fc_inet_cidr_pton_ipv6(const char *fc_src, u_char *fc_dst, size_t fc_size)
{
	static const char fc_xdigits_l[] = "0123456789abcdef",
				fc_xdigits_u[] = "0123456789ABCDEF";
	u_char		fc_tmp[NS_IN6ADDRSZ],
			   *fc_tp,
			   *fc_endp,
			   *fc_colonp;
	const char *fc_xdigits,
			   *fc_curtok;
	int			fc_ch,
				fc_saw_xdigit;
	u_int		fc_val;
	int			fc_digits;
	int			fc_bits;

	if (fc_size < NS_IN6ADDRSZ)
		goto emsgsize;

	memset((fc_tp = fc_tmp), '\0', NS_IN6ADDRSZ);
	fc_endp = fc_tp + NS_IN6ADDRSZ;
	fc_colonp = NULL;
	/* 前导::需要一些特殊处理。*/
	if (*fc_src == ':')
		if (*++fc_src != ':')
			goto enoent;
	fc_curtok = fc_src;
	fc_saw_xdigit = 0;
	fc_val = 0;
	fc_digits = 0;
	fc_bits = -1;
	while ((fc_ch = *fc_src++) != '\0')
	{
		const char *fc_pch;

		if ((fc_pch = strchr((fc_xdigits = fc_xdigits_l), fc_ch)) == NULL)
			fc_pch = strchr((fc_xdigits = fc_xdigits_u), fc_ch);
		if (fc_pch != NULL)
		{
			fc_val <<= 4;
			fc_val |= (fc_pch - fc_xdigits);
			if (++fc_digits > 4)
				goto enoent;
			fc_saw_xdigit = 1;
			continue;
		}
		if (fc_ch == ':')
		{
			fc_curtok = fc_src;
			if (!fc_saw_xdigit)
			{
				if (fc_colonp)
					goto enoent;
				fc_colonp = fc_tp;
				continue;
			}
			else if (*fc_src == '\0')
				goto enoent;
			if (fc_tp + NS_INT16SZ > fc_endp)
				goto enoent;
			*fc_tp++ = (u_char) (fc_val >> 8) & 0xff;
			*fc_tp++ = (u_char) fc_val & 0xff;
			fc_saw_xdigit = 0;
			fc_digits = 0;
			fc_val = 0;
			continue;
		}
		if (fc_ch == '.' && ((fc_tp + NS_INADDRSZ) <= fc_endp) &&
			fc_getv4(fc_curtok, fc_tp, &fc_bits) > 0)
		{
			fc_tp += NS_INADDRSZ;
			fc_saw_xdigit = 0;
			break;				/* '\0'被inet_pton4()看到。*/
		}
		if (fc_ch == '/' && fc_getbits(fc_src, &fc_bits) > 0)
			break;
		goto enoent;
	}
	if (fc_saw_xdigit)
	{
		if (fc_tp + NS_INT16SZ > fc_endp)
			goto enoent;
		*fc_tp++ = (u_char) (fc_val >> 8) & 0xff;
		*fc_tp++ = (u_char) fc_val & 0xff;
	}
	if (fc_bits == -1)
		fc_bits = 128;

	fc_endp = fc_tmp + 16;

	if (fc_colonp != NULL)
	{
		/* 
		 * 由于一些memmove()错误地无法处理重叠
		 * 区域，我们将手动进行移动。
		 */
		const int	fc_n = fc_tp - fc_colonp;
		int			fc_i;

		if (fc_tp == fc_endp)
			goto enoent;
		for (fc_i = 1; fc_i <= fc_n; fc_i++)
		{
			fc_endp[-fc_i] = fc_colonp[fc_n - fc_i];
			fc_colonp[fc_n - fc_i] = 0;
		}
		fc_tp = fc_endp;
	}
	if (fc_tp != fc_endp)
		goto enoent;

	/* 
	 * 复制结果。
	 */
	memcpy(fc_dst, fc_tmp, NS_IN6ADDRSZ);

	return fc_bits;

enoent:
	errno = ENOENT;
	return -1;

emsgsize:
	errno = EMSGSIZE;
	return -1;
}
